Conversation
|
Invitation URL: |
Test Results 72 files 493 suites 0s ⏱️ Results for commit 7aeef13. ♻️ This comment has been updated with latest results. |
|
@tpmanley |
|
Minimum allowed coverage is Generated by 🐒 cobertura-action against 7aeef13 |
First of all, I corrected all the errors. |
|
@ctowns |
|
@HunsupJung when you're setting up the test, you can make a mock device with the following profile Then run the test as normal. This will make the tested profile include the optional capability. You can add as many optional capabilities as you want in this way. |
drivers/SmartThings/matter-lock/src/test/test_new_matter_lock_battery.lua
Outdated
Show resolved
Hide resolved
drivers/SmartThings/matter-lock/src/test/test_new_matter_lock_battery.lua
Outdated
Show resolved
Hide resolved
drivers/SmartThings/matter-lock/src/test/test_new_matter_lock_battery.lua
Outdated
Show resolved
Hide resolved
a90c386 to
b331d2d
Compare
|
@tpmanley |
|
Thanks for resolving my two comments. There are a couple of conflicts right now so what I'd recommend is squash and rebase your changes into a single commit on top of latest |
199cfbd to
98a69db
Compare
|
@tpmanley |
| BATTERY_SUPPORT = "__BATTERY_SUPPORT", | ||
| } | ||
|
|
||
| local DoorLockFeatureMapAttr = {ID = 0xFFFC, cluster = DoorLock.ID} |
There was a problem hiding this comment.
@gharveymn it doesn't seem like we have any way to represent this naturally in the generated clusters. Do you have any context for why? And do you have any recomendations on how to integrate it in the driver "naturally"?
I'm thinking we may want to handle it as:
| local DoorLockFeatureMapAttr = {ID = 0xFFFC, cluster = DoorLock.ID} | |
| local DoorLock.attributes.FeatureMap = { | |
| ID = 0xFFFC, | |
| NAME = "FeatureMap", | |
| _cluster = require "st.matter.clusters.DoorLock", | |
| base_type = require "st.matter.clusters.DoorLock.types.Feature", | |
| } |
I'm mainly thinking of 1. whether/how we'd subscribe to this, and 2. whether we can make unit tests for it.
There was a problem hiding this comment.
Not really too much context. I wasn't involved in the PRs which did the filtering. We also filter two other attributes: GeneratedCommandList and ClusterRevision, but those make sense since we don't act as a server in Lua-land.
I don't really see a reason not to add cluster-specific attribute definitions in scripting-engine for each of these where the base type is types.Feature. You may run into an issue where the Feature type doesn't exist for certain clusters, but you can just work around that by making the base type U32 or something.
The way you proposed would be a start, but I think you would be missing certain functions which would be a blocker for (1) and (2). Perhaps you can make some adjustments to the ZAP generation for scripting-engine locally, and then export the attribute definition as an embedded cluster attribute for now.
There was a problem hiding this comment.
@HunsupJung to integrate this more cleanly, I think we should add the following file DoorLock/server/attributes/FeatureMap.lua for the embedded cluster definition:
local cluster_base = require "st.matter.cluster_base"
local data_types = require "st.matter.data_types"
local TLVParser = require "st.matter.TLV.TLVParser"
local FeatureMap = {
ID = 0xFFFC,
NAME = "FeatureMap",
_cluster = require "st.matter.clusters.DoorLock",
base_type = require "st.matter.clusters.DoorLock.types.Feature",
}
function FeatureMap:augment_type(data_type_obj)
for i, v in ipairs(data_type_obj.elements) do
data_type_obj.elements[i] = data_types.validate_or_build_type(v, FeatureMap.element_type)
end
end
function FeatureMap:new_value(...)
local o = self.base_type(table.unpack({...}))
return o
end
function FeatureMap:read(device, endpoint_id)
return cluster_base.read(
device,
endpoint_id,
self._cluster.ID,
self.ID,
nil
)
end
function FeatureMap:subscribe(device, endpoint_id)
return cluster_base.subscribe(
device,
endpoint_id,
self._cluster.ID,
self.ID,
nil
)
end
function FeatureMap:set_parent_cluster(cluster)
self._cluster = cluster
return self
end
function FeatureMap:build_test_report_data(
device,
endpoint_id,
value,
status
)
local data = data_types.validate_or_build_type(value, self.base_type)
return cluster_base.build_test_report_data(
device,
endpoint_id,
self._cluster.ID,
self.ID,
data,
status
)
end
function FeatureMap:deserialize(tlv_buf)
local data = TLVParser.decode_tlv(tlv_buf)
return data
end
setmetatable(FeatureMap, {__call = FeatureMap.new_value, __index = FeatureMap.base_type})
return FeatureMap
Then we can maybe, at the top of the file, update the version check to something like:
if version.api < 10 then
clusters.DoorLock = require "DoorLock"
else
clusters.DoorLock.attributes.FeatureMap = require "DoorLock.server.attributes.FeatureMap"
end
Besides making this "cleaner", I think we should create unit tests for this newly introduced logic, and we will need this to create proper unit tests.
There was a problem hiding this comment.
The code below is not working. Because cluster.DoorLock.attributes.FeatureMap is not declared, override is not possible.
clusters.DoorLock.attributes.FeatureMap = require "DoorLock.server.attributes.FeatureMap"
It would be better to modify this part in the subsequent scripting-engine update.
https://samsung.slack.com/archives/C063J01GHR6/p1765382793593279?thread_ts=1732583132.684279&cid=C063J01GHR6
There was a problem hiding this comment.
I agree that we should modify the scripting-engine. However, I think we should be able to include unit tests for this new profile changing feature to ensure that everything works properly.
That said, I looked into your issues and made the following changes:
local cluster_base = require "st.matter.cluster_base"
local data_types = require "st.matter.data_types"
local TLVParser = require "st.matter.TLV.TLVParser"
local FeatureMap = {
ID = 0xFFFC,
NAME = "FeatureMap",
_cluster = require "DoorLock",
base_type = require "DoorLock.types.Feature",
}
function FeatureMap:augment_type(data_type_obj)
for i, v in ipairs(data_type_obj.elements) do
data_type_obj.elements[i] = data_types.validate_or_build_type(v, FeatureMap.element_type)
end
end
function FeatureMap:new_value(...)
local o = self.base_type(table.unpack({...}))
return o
end
function FeatureMap:read(device, endpoint_id)
return cluster_base.read(
device,
endpoint_id,
self._cluster.ID,
self.ID,
nil
)
end
function FeatureMap:subscribe(device, endpoint_id)
return cluster_base.subscribe(
device,
endpoint_id,
self._cluster.ID,
self.ID,
nil
)
end
function FeatureMap:set_parent_cluster(cluster)
self._cluster = cluster
return self
end
function FeatureMap:build_test_report_data(
device,
endpoint_id,
value,
status
)
local data = data_types.validate_or_build_type(value, self.base_type)
return cluster_base.build_test_report_data(
device,
endpoint_id,
self._cluster.ID,
self.ID,
data,
status
)
end
function FeatureMap:deserialize(tlv_buf)
local data = TLVParser.decode_tlv(tlv_buf)
return data
end
setmetatable(FeatureMap, {__call = FeatureMap.new_value, __index = FeatureMap.base_type})
return FeatureMap
should be the file, and
-- Copyright 2025 SmartThings, Inc.
-- Licensed under the Apache License, Version 2.0
local cluster_base = require "st.matter.cluster_base"
local DoorLockServerAttributes = require "DoorLock.server.attributes"
local DoorLockServerCommands = require "DoorLock.server.commands"
local DoorLockClientCommands = require "DoorLock.client.commands"
local DoorLockEvents = require "DoorLock.server.events"
local DoorLockTypes = require "DoorLock.types"
local DoorLock = {}
DoorLock.ID = 0x0101
DoorLock.NAME = "DoorLock"
DoorLock.server = {}
DoorLock.client = {}
DoorLock.server.attributes = DoorLockServerAttributes:set_parent_cluster(DoorLock)
DoorLock.server.commands = DoorLockServerCommands:set_parent_cluster(DoorLock)
DoorLock.client.commands = DoorLockClientCommands:set_parent_cluster(DoorLock)
DoorLock.server.events = DoorLockEvents:set_parent_cluster(DoorLock)
DoorLock.types = DoorLockTypes
function DoorLock:get_attribute_by_id(attr_id)
local attr_id_map = {
[0x0000] = "LockState",
[0x0001] = "LockType",
[0x0002] = "ActuatorEnabled",
[0x0003] = "DoorState",
[0x0004] = "DoorOpenEvents",
[0x0005] = "DoorClosedEvents",
[0x0006] = "OpenPeriod",
[0x0011] = "NumberOfTotalUsersSupported",
[0x0012] = "NumberOfPINUsersSupported",
[0x0013] = "NumberOfRFIDUsersSupported",
[0x0014] = "NumberOfWeekDaySchedulesSupportedPerUser",
[0x0015] = "NumberOfYearDaySchedulesSupportedPerUser",
[0x0016] = "NumberOfHolidaySchedulesSupported",
[0x0017] = "MaxPINCodeLength",
[0x0018] = "MinPINCodeLength",
[0x0019] = "MaxRFIDCodeLength",
[0x001A] = "MinRFIDCodeLength",
[0x001B] = "CredentialRulesSupport",
[0x001C] = "NumberOfCredentialsSupportedPerUser",
[0x0021] = "Language",
[0x0022] = "LEDSettings",
[0x0023] = "AutoRelockTime",
[0x0024] = "SoundVolume",
[0x0025] = "OperatingMode",
[0x0026] = "SupportedOperatingModes",
[0x0027] = "DefaultConfigurationRegister",
[0x0028] = "EnableLocalProgramming",
[0x0029] = "EnableOneTouchLocking",
[0x002A] = "EnableInsideStatusLED",
[0x002B] = "EnablePrivacyModeButton",
[0x002C] = "LocalProgrammingFeatures",
[0x0030] = "WrongCodeEntryLimit",
[0x0031] = "UserCodeTemporaryDisableTime",
[0x0032] = "SendPINOverTheAir",
[0x0033] = "RequirePINforRemoteOperation",
[0x0035] = "ExpiringUserTimeout",
[0x0080] = "AliroReaderVerificationKey",
[0x0081] = "AliroReaderGroupIdentifier",
[0x0082] = "AliroReaderGroupSubIdentifier",
[0x0083] = "AliroExpeditedTransactionSupportedProtocolVersions",
[0x0084] = "AliroGroupResolvingKey",
[0x0085] = "AliroSupportedBLEUWBProtocolVersions",
[0x0086] = "AliroBLEAdvertisingVersion",
[0x0087] = "NumberOfAliroCredentialIssuerKeysSupported",
[0x0088] = "NumberOfAliroEndpointKeysSupported",
[0xFFF9] = "AcceptedCommandList",
[0xFFFA] = "EventList",
[0xFFFB] = "AttributeList",
[0xFFFC] = "FeatureMap",
}
local attr_name = attr_id_map[attr_id]
if attr_name ~= nil then
return self.attributes[attr_name]
end
return nil
end
function DoorLock:get_server_command_by_id(command_id)
local server_id_map = {
[0x0000] = "LockDoor",
[0x0001] = "UnlockDoor",
[0x0003] = "UnlockWithTimeout",
[0x000B] = "SetWeekDaySchedule",
[0x000C] = "GetWeekDaySchedule",
[0x000D] = "ClearWeekDaySchedule",
[0x000E] = "SetYearDaySchedule",
[0x000F] = "GetYearDaySchedule",
[0x0010] = "ClearYearDaySchedule",
[0x0011] = "SetHolidaySchedule",
[0x0012] = "GetHolidaySchedule",
[0x0013] = "ClearHolidaySchedule",
[0x001A] = "SetUser",
[0x001B] = "GetUser",
[0x001D] = "ClearUser",
[0x0022] = "SetCredential",
[0x0024] = "GetCredentialStatus",
[0x0026] = "ClearCredential",
[0x0027] = "UnboltDoor",
[0x0028] = "SetAliroReaderConfig",
[0x0029] = "ClearAliroReaderConfig",
}
if server_id_map[command_id] ~= nil then
return self.server.commands[server_id_map[command_id]]
end
return nil
end
function DoorLock:get_client_command_by_id(command_id)
local client_id_map = {
[0x000C] = "GetWeekDayScheduleResponse",
[0x000F] = "GetYearDayScheduleResponse",
[0x0012] = "GetHolidayScheduleResponse",
[0x001C] = "GetUserResponse",
[0x0023] = "SetCredentialResponse",
[0x0025] = "GetCredentialStatusResponse",
}
if client_id_map[command_id] ~= nil then
return self.client.commands[client_id_map[command_id]]
end
return nil
end
function DoorLock:get_event_by_id(event_id)
local event_id_map = {
[0x0000] = "DoorLockAlarm",
[0x0001] = "DoorStateChange",
[0x0002] = "LockOperation",
[0x0003] = "LockOperationError",
[0x0004] = "LockUserChange",
}
if event_id_map[event_id] ~= nil then
return self.server.events[event_id_map[event_id]]
end
return nil
end
DoorLock.attribute_direction_map = {
["LockState"] = "server",
["LockType"] = "server",
["ActuatorEnabled"] = "server",
["DoorState"] = "server",
["DoorOpenEvents"] = "server",
["DoorClosedEvents"] = "server",
["OpenPeriod"] = "server",
["NumberOfTotalUsersSupported"] = "server",
["NumberOfPINUsersSupported"] = "server",
["NumberOfRFIDUsersSupported"] = "server",
["NumberOfWeekDaySchedulesSupportedPerUser"] = "server",
["NumberOfYearDaySchedulesSupportedPerUser"] = "server",
["NumberOfHolidaySchedulesSupported"] = "server",
["MaxPINCodeLength"] = "server",
["MinPINCodeLength"] = "server",
["MaxRFIDCodeLength"] = "server",
["MinRFIDCodeLength"] = "server",
["CredentialRulesSupport"] = "server",
["NumberOfCredentialsSupportedPerUser"] = "server",
["Language"] = "server",
["LEDSettings"] = "server",
["AutoRelockTime"] = "server",
["SoundVolume"] = "server",
["OperatingMode"] = "server",
["SupportedOperatingModes"] = "server",
["DefaultConfigurationRegister"] = "server",
["EnableLocalProgramming"] = "server",
["EnableOneTouchLocking"] = "server",
["EnableInsideStatusLED"] = "server",
["EnablePrivacyModeButton"] = "server",
["LocalProgrammingFeatures"] = "server",
["WrongCodeEntryLimit"] = "server",
["UserCodeTemporaryDisableTime"] = "server",
["SendPINOverTheAir"] = "server",
["RequirePINforRemoteOperation"] = "server",
["ExpiringUserTimeout"] = "server",
["AliroReaderVerificationKey"] = "server",
["AliroReaderGroupIdentifier"] = "server",
["AliroReaderGroupSubIdentifier"] = "server",
["AliroExpeditedTransactionSupportedProtocolVersions"] = "server",
["AliroGroupResolvingKey"] = "server",
["AliroSupportedBLEUWBProtocolVersions"] = "server",
["AliroBLEAdvertisingVersion"] = "server",
["NumberOfAliroCredentialIssuerKeysSupported"] = "server",
["NumberOfAliroEndpointKeysSupported"] = "server",
["AcceptedCommandList"] = "server",
["EventList"] = "server",
["AttributeList"] = "server",
["FeatureMap"] = "server",
}
DoorLock.command_direction_map = {
["LockDoor"] = "server",
["UnlockDoor"] = "server",
["UnlockWithTimeout"] = "server",
["SetWeekDaySchedule"] = "server",
["GetWeekDaySchedule"] = "server",
["ClearWeekDaySchedule"] = "server",
["SetYearDaySchedule"] = "server",
["GetYearDaySchedule"] = "server",
["ClearYearDaySchedule"] = "server",
["SetHolidaySchedule"] = "server",
["GetHolidaySchedule"] = "server",
["ClearHolidaySchedule"] = "server",
["SetUser"] = "server",
["GetUser"] = "server",
["ClearUser"] = "server",
["SetCredential"] = "server",
["GetCredentialStatus"] = "server",
["ClearCredential"] = "server",
["UnboltDoor"] = "server",
["SetAliroReaderConfig"] = "server",
["ClearAliroReaderConfig"] = "server",
["GetWeekDayScheduleResponse"] = "client",
["GetYearDayScheduleResponse"] = "client",
["GetHolidayScheduleResponse"] = "client",
["GetUserResponse"] = "client",
["SetCredentialResponse"] = "client",
["GetCredentialStatusResponse"] = "client",
}
DoorLock.FeatureMap = DoorLock.types.Feature
function DoorLock.are_features_supported(feature, feature_map)
if (DoorLock.FeatureMap.bits_are_valid(feature)) then
return (feature & feature_map) == feature
end
return false
end
local attribute_helper_mt = {}
attribute_helper_mt.__index = function(self, key)
local direction = DoorLock.attribute_direction_map[key]
if direction == nil then
error(string.format("Referenced unknown attribute %s on cluster %s", key, DoorLock.NAME))
end
return DoorLock[direction].attributes[key]
end
DoorLock.attributes = {}
setmetatable(DoorLock.attributes, attribute_helper_mt)
local command_helper_mt = {}
command_helper_mt.__index = function(self, key)
local direction = DoorLock.command_direction_map[key]
if direction == nil then
error(string.format("Referenced unknown command %s on cluster %s", key, DoorLock.NAME))
end
return DoorLock[direction].commands[key]
end
DoorLock.commands = {}
setmetatable(DoorLock.commands, command_helper_mt)
local event_helper_mt = {}
event_helper_mt.__index = function(self, key)
return DoorLock.server.events[key]
end
DoorLock.events = {}
setmetatable(DoorLock.events, event_helper_mt)
setmetatable(DoorLock, {__index = cluster_base})
return DoorLock
should be the init.lua file. Then this will work:
if version.api < 10 then
clusters.DoorLock = require "DoorLock"
else
clusters.DoorLock.attributes.FeatureMap = require "DoorLock.server.attributes.FeatureMap"
end
There was a problem hiding this comment.
I think we should do the above and then add the following tests.
- Ensure the the profiles updates when the feature map changes.
- Ensure the profile update does not occur when the feature map doesn't change but we get a handler event
f6dac7a to
b983f96
Compare
| if version.api >= 15 and version.rpc >= 9 then | ||
| match_profile_modular(driver, device) | ||
| end |
There was a problem hiding this comment.
This will not work currently, since it will no longer check for profiling data (that function is done at the start of match_profile). We should either add a parameter to match_profile called modular_only or something, or just move that profiling data function into the sub match profile functions.
There was a problem hiding this comment.
It does not need to read PowerSource cluster again since it is only related to the DoorLock cluster. (I mean that match_profile can be used here.)
If it needs to check DPS feature, I will add it in DPS PR.
There was a problem hiding this comment.
I still think we need this profiling_data check as a safety precaution.
Since we will subscribe to this attribute and the power source attribute list attribute when a device is first onboarded, it is possible that this handler will run before the power source attribute list handler. We cannot guarantee that ordering. This may cause 2 profile updates in a short period, which causes significant latency, so we should avoid this.
There was a problem hiding this comment.
It makes sense. I will consider this part and modify.
There was a problem hiding this comment.
I do not think we need to check if the profile is a modular profile, I think we need to add a way to handle the profiling_data_still_required check.
Basically, I suggest that we edit the match_profile function:
local function match_profile(driver, device, ignore_static_profiling)
if profiling_data_still_required(device) then return end
if version.api >= 15 and version.rpc >= 9 then
match_profile_modular(driver, device)
elseif ignore_static_profiling ~= true then
match_profile_switch(driver, device)
end
end
and change this to:
match_profile(driver, device, true)
I think we can remove the new IS_MODULAR_PROFILE field.
6b496a8 to
8da3bdc
Compare
5ab11d7 to
98a69db
Compare
Signed-off-by: Hunsup Jung <hunsup.jung@samsung.com>
8f3a239 to
e6c9a26
Compare
e6c9a26 to
56007f0
Compare
Signed-off-by: Hunsup Jung <hunsup.jung@samsung.com>
56007f0 to
7aeef13
Compare
|
@hcarter-775 |
Type of Change
Checklist
Description of Change
Summary of Completed Tests